/*
 * Zed Attack Proxy (ZAP) and its related class files.
 *
 * ZAP is an HTTP/HTTPS proxy for assessing web application security.
 *
 * Copyright 2014 The ZAP Development Team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.zaproxy.zap.extension.keyboard;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.KeyStroke;
import org.parosproxy.paros.Constant;
import org.zaproxy.zap.view.StandardFieldsDialog;

@SuppressWarnings("serial")
public class DialogEditShortcut extends StandardFieldsDialog {

    private static final String FIELD_ACTION = "keyboard.dialog.label.action";
    private static final String FIELD_KEY = "keyboard.dialog.label.key";
    private static final String FIELD_CONTROL = "keyboard.dialog.label.control";
    private static final String FIELD_ALT = "keyboard.dialog.label.alt";
    private static final String FIELD_SHIFT = "keyboard.dialog.label.shift";
    private static final String FIELD_INFO = "keyboard.dialog.label.info";

    private static final long serialVersionUID = 1L;

    private KeyboardShortcut shortcut;
    private KeyboardShortcutTableModel model;

    /**
     * Constructs a modal {@code DialogEditShortcut}, with the given {@code Window} as its owner.
     *
     * @param owner the owner of the dialogue
     * @since 2.5.0
     */
    public DialogEditShortcut(Window owner) {
        super(owner, "keyboard.dialog.title", new Dimension(300, 200), true);
    }

    public DialogEditShortcut(Frame owner) {
        super(owner, "keyboard.dialog.title", new Dimension(300, 200));
    }

    public void init(KeyboardShortcut shortcut, KeyboardShortcutTableModel model) {
        this.shortcut = shortcut;
        this.model = model;

        this.removeAllFields();

        ActionListener listener =
                new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        checkDuplicate();
                    }
                };

        this.addReadOnlyField(FIELD_ACTION, shortcut.getName(), false);
        this.addComboField(FIELD_KEY, getKeyList(), getKey(shortcut.getKeyStroke()));
        this.addFieldListener(FIELD_KEY, listener);
        this.addCheckBoxField(
                FIELD_CONTROL, this.isModifier(shortcut.getKeyStroke(), InputEvent.CTRL_DOWN_MASK));
        this.addFieldListener(FIELD_CONTROL, listener);
        this.addCheckBoxField(
                FIELD_ALT, this.isModifier(shortcut.getKeyStroke(), InputEvent.ALT_DOWN_MASK));
        this.addFieldListener(FIELD_ALT, listener);
        this.addCheckBoxField(
                FIELD_SHIFT, this.isModifier(shortcut.getKeyStroke(), InputEvent.SHIFT_DOWN_MASK));
        this.addFieldListener(FIELD_SHIFT, listener);
        this.addReadOnlyField(FIELD_INFO, "", true);

        this.getField(FIELD_INFO).setForeground(Color.RED);
    }

    @Override
    public String getSaveButtonText() {
        // Not really saving, just setting here..
        return Constant.messages.getString("keyboard.dialog.button.save");
    }

    /**
     * Checks to see if the chosen shortcut is already being used and if so shows a message warning
     * the user
     */
    private void checkDuplicate() {
        KeyboardShortcut ks = this.getDuplicate();
        if (ks != null) {
            this.setFieldValue(
                    FIELD_INFO,
                    Constant.messages.getString("keyboard.dialog.warning.dup", ks.getName()));
        } else {
            this.setFieldValue(FIELD_INFO, "");
        }
    }

    private KeyboardShortcut getDuplicate() {
        KeyStroke chosenKs = this.getKeyStroke();
        if (chosenKs != null) {
            for (KeyboardShortcut ks : this.model.getElements()) {
                if (!ks.equals(this.shortcut)) {
                    KeyStroke testKs = ks.getKeyStroke();
                    if (testKs != null
                            && chosenKs.getKeyCode() == testKs.getKeyCode()
                            && chosenKs.getModifiers() == testKs.getModifiers()) {
                        return ks;
                    }
                }
            }
        }
        return null;
    }

    private List<String> getKeyList() {
        List<String> list = new ArrayList<>();
        list.add(""); // Always start with a blank option - means no key set
        for (char c = 'A'; c <= 'Z'; c++) {
            list.add(String.valueOf(c));
        }
        // Numbers
        for (int i = 0; i <= 9; i++) {
            list.add(String.valueOf(i));
        }
        // Non Alphnumeric keys
        list.add("-");
        list.add("=");
        list.add("[");
        list.add("]");
        list.add(";");
        list.add("'");
        list.add("#");
        list.add(",");
        list.add(".");
        list.add("/");
        // Function keys
        for (int i = 1; i <= 12; i++) {
            list.add("F" + i);
        }
        // Arrow keys
        list.add(Constant.messages.getString("keyboard.key.up"));
        list.add(Constant.messages.getString("keyboard.key.down"));
        list.add(Constant.messages.getString("keyboard.key.left"));
        list.add(Constant.messages.getString("keyboard.key.right"));

        return list;
    }

    private String getKey(KeyStroke ks) {
        if (ks != null) {
            return KeyboardMapping.keyString(ks.getKeyCode());
        }
        return "";
    }

    private char selectedKey() {
        return KeyboardMapping.keyCode(this.getStringValue(FIELD_KEY));
    }

    private boolean isModifier(KeyStroke ks, int modifier) {
        if (ks != null) {
            return (ks.getModifiers() & modifier) != 0;
        }
        return false;
    }

    public KeyStroke getKeyStroke() {
        KeyStroke ks = null;
        int keyCode = selectedKey();
        int modifiers = 0;

        if (keyCode != 0) {
            if (this.getBoolValue(FIELD_CONTROL)) {
                modifiers |= InputEvent.CTRL_DOWN_MASK;
            }
            if (this.getBoolValue(FIELD_ALT)) {
                modifiers |= InputEvent.ALT_DOWN_MASK;
            }
            if (this.getBoolValue(FIELD_SHIFT)) {
                modifiers |= InputEvent.SHIFT_DOWN_MASK;
            }
            ks = KeyStroke.getKeyStroke(keyCode, modifiers, false);
        }
        return ks;
    }

    @Override
    public void save() {
        KeyboardShortcut ksDup = this.getDuplicate();
        if (ksDup != null) {
            // used for another menu item, so remove it from that
            ksDup.setKeyStroke(null);
        }
        KeyStroke ks = getKeyStroke();
        shortcut.setKeyStroke(ks);
    }

    @Override
    public String validateFields() {
        // Nothing to do
        return null;
    }
}
